/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "ScanMethodHunt.h"

//--------------------------------------------------------------------

ScanMethodHunt::ScanMethodHunt(void)
{
	myType = __wmtHunt;

	xHuntParams.Initialize();
}

ScanMethodHunt::~ScanMethodHunt(void)
{
}

//--------------------------------------------------------------------

int ScanMethodHunt::InternalInit(void)
{
	return MSR_ERR_OK;
}

//--------------------------------------------------------------------

int ScanMethodHunt::InternalExecute(void)
{
	DWORD ts, te, td, tsx;
	BYTE dnsMAC[6];

	dataProcessed = false;
	ts = GetTickCount();
	tsx = ts;

	myIP = winPcap->GetLocalIP();

	addrList->Clear(); // nu ma intereseaza adrese, le aflu pe parcurs

	sspc.sspWinPcap              = winPcap;
	sspc.sspPackBuilderEx        = packetBuilderEx;
	sspc.sspPackBuilderEx6       = packetBuilderEx6;
	sspc.sspPacketBuilderPromisc = packetBuilderPromisc;
	sspc.sspWorkCfg              = config;
	sspc.sspArpCacheList         = arpCacheList;
	sspc.sspListOfAddr           = addrList;
	sspc.sspMachInfoList         = machInfoList;
	sspc.sspMsgWnd               = hWnd;
	sspc.sspWmiWorker            = wmiWorker;
	sspc.sspNotifNetBios         = notifNetBios;
	sspc.sspNotifPing            = notifPing;
	sspc.sspNotifTcp             = notifTcp;
	sspc.sspNotifUdp             = notifUdp;
	sspc.sspWorkPhase            = __wptScan;

	sspc.sspData                 = scanParameters;

	// set included scan methods to embedded mode
	scanMethodArp.SetEmbeddedMode(true); // foarte important, altfel apare EndCapture si salutis
	scanMethodSyn.SetEmbeddedMode(true); // foarte important, altfel apare EndCapture si salutis
	scanMethodDns.SetEmbeddedMode(true); // foarte important, altfel apare EndCapture si salutis

	// find the MAC of DNS
	if(xHuntParams.xPerformDnsScan){
		sspc.sspWorkPhase = __wptArp;
		scanMethodArp.Start(&sspc);
		scanMethodArp.Stop();
		scanMethodArp.FillDnsServerMAC(dnsMAC);
		winPcap->SetDnsMAC(dnsMAC);
	}

	// prepare for scan phase
	sspc.sspWorkPhase = __wptScan;

	// the main loop
	while(!stopStopStop){
		CheckResponses();

		te = GetTickCount();

		td = ComputeDeltaTime(ts, te);
		if(td>3000){
			if(dataProcessed){
				if(hWnd!=NULL)
					SendMessage(hWnd, CHS_MSG_UPDATE_DATA, 2, 0);
				dataProcessed = false;
				tsx = te;
			}
			ts = te;
		}

		td = ComputeDeltaTime(tsx, te);
		if(td>60000){
			if(hWnd!=NULL)
				SendMessage(hWnd, CHS_MSG_UPDATE_DATA, 2, 0);
			tsx = te;
		}
	}

	if(hWnd!=NULL)
		SendMessage(hWnd, CHS_MSG_UPDATE_DATA, 2, 0);

	return MSR_ERR_OK;
}

//--------------------------------------------------------------------

void ScanMethodHunt::AddMachineAndPort(BYTE* srcIpIn, BYTE* srcMacIn, WORD portIn, WorkMethodType methodIn)
{
	if(srcIpIn==NULL) return;
	if(srcMacIn==NULL) return;

	addrX.Initialize();
	addrX.addrIP.Set(srcIpIn[0], srcIpIn[1], srcIpIn[2], srcIpIn[3]);
	addrX.addrMAC.LoadFromBytePtr(srcMacIn);
	addrX.alive = true;
	addrX.respReceived = true;

	machX.Initialize();
	machX.address.LoadFromPointer(&addrX);

	if(xHuntParams.xIpLocator!=NULL && xHuntParams.xFindLocation){
		if(xHuntParams.xIpLocator->xDataLoaded)
			machX.countryCode = xHuntParams.xIpLocator->GetCountry(addrX.addrIP.GetD());
	}

	if(portIn>0){
		portX.Initialize();
		portX.method = methodIn;
		portX.port = portIn;
		machX.AddPortInfoNoDuplicates(&portX);
	}

	machInfoList->Add(&machX);
}

//---------------------------------------------------------------------------

void ScanMethodHunt::ProcessArpAny(BYTE* dataIn, int sizeIn)
{
	if(sizeIn<42) return; // incomplete ARP packet

	FullARPPacket *arpp;
	in_addr      inAddr;
	MMachineInfo *newMach;

	arpp = (FullARPPacket*)dataIn;
	if(ntohs(arpp->arpCode)!=1) return; // not ARP Request

	inAddr.S_un.S_un_b.s_b1 = arpp->receiverPA[0];
	inAddr.S_un.S_un_b.s_b2 = arpp->receiverPA[1];
	inAddr.S_un.S_un_b.s_b3 = arpp->receiverPA[2];
	inAddr.S_un.S_un_b.s_b4 = arpp->receiverPA[3];

	if(myIP!=inAddr.S_un.S_addr) return; // not for me

	// add machine to be hunted ...
	AddMachineAndPort(arpp->senderPA, arpp->senderHA, 0, __wmtHunt);

	// ... and refresh display
	if(hWnd!=NULL)
		SendMessage(hWnd, CHS_MSG_UPDATE_DATA, 2, 0);

	newMach = machInfoList->GetMachineInfoByIpAddr(inAddr.S_un.S_addr);
	if(newMach==NULL) return; // should not happend !

	if(newMach->hunted){
		if(hWnd!=NULL)
			SendMessage(hWnd, CHS_MSG_HUNT_MACHINE, 3, (LPARAM)(void*)newMach);
	}
	else Hunt(newMach);

	dataProcessed = true;		
}

//--------------------------------------------------------------------

void ScanMethodHunt::ProcessTcp(BYTE* dataIn, int sizeIn, BYTE* srcMAC)
{
	if(sizeIn<20) return; // incomplete IP packet

	IPHeaderPG   *iph;
	TCPHeaderPG  *tcph;
	int          dataOffset;
	in_addr      inAddr;
	MMachineInfo *newMach;

	iph = (IPHeaderPG*)dataIn;
	dataOffset  = (int)(iph->HLV & 0x0F);
	dataOffset *= 4;
	if(sizeIn<(dataOffset + 20)) return;	// incomplete IP+TCP packet

	tcph = (TCPHeaderPG*)&dataIn[dataOffset];

	if(tcph->Flags!=TH_SYN) return; // not a SYN packet

	inAddr.S_un.S_un_b.s_b1 = iph->Source[0];
	inAddr.S_un.S_un_b.s_b2 = iph->Source[1];
	inAddr.S_un.S_un_b.s_b3 = iph->Source[2];
	inAddr.S_un.S_un_b.s_b4 = iph->Source[3];

	if(myIP==inAddr.S_un.S_addr) return; // the packet has originated from my IP

	// add machine to be hunted ...
	AddMachineAndPort(iph->Source, srcMAC, ntohs(tcph->DestinationPort), __wmtHunt);

	// ... and refresh display
	if(hWnd!=NULL)
		SendMessage(hWnd, CHS_MSG_UPDATE_DATA, 2, 0);

	newMach = machInfoList->GetMachineInfoByIpAddr(inAddr.S_un.S_addr);
	if(newMach==NULL) return; // should not happend !

	if(newMach->hunted){
		if(hWnd!=NULL)
			SendMessage(hWnd, CHS_MSG_HUNT_MACHINE, 3, (LPARAM)(void*)newMach);
	}
	else Hunt(newMach);

	dataProcessed = true;		
}

//---------------------------------------------------------------------------

void ScanMethodHunt::ProcessIcmpAny(BYTE* dataIn, int sizeIn, BYTE* srcMAC)
{
	IPHeaderPG *iph;
	ICMPHeaderPG* icmph;
	int dataOffset;
	in_addr      inAddr;
	MMachineInfo *newMach;

	iph = (IPHeaderPG*)dataIn;
	dataOffset  = (int)(iph->HLV & 0x0F);
	dataOffset *= 4;
	if(sizeIn<(dataOffset + 8)){
		// incomplete packet
		return;
	}

	icmph = (ICMPHeaderPG*)&dataIn[dataOffset];

	if(icmph->Type!=0x08) return; // not echo request

	inAddr.S_un.S_un_b.s_b1 = iph->Source[0];
	inAddr.S_un.S_un_b.s_b2 = iph->Source[1];
	inAddr.S_un.S_un_b.s_b3 = iph->Source[2];
	inAddr.S_un.S_un_b.s_b4 = iph->Source[3];

	if(myIP==inAddr.S_un.S_addr) return; // the packet has originated from my IP

	// add machine to be hunted ...
	AddMachineAndPort(iph->Source, srcMAC, 0, __wmtHunt);

	// ... and refresh display
	if(hWnd!=NULL)
		SendMessage(hWnd, CHS_MSG_UPDATE_DATA, 2, 0);

	newMach = machInfoList->GetMachineInfoByIpAddr(inAddr.S_un.S_addr);
	if(newMach==NULL) return; // should not happend !

	if(newMach->hunted){
		if(hWnd!=NULL)
			SendMessage(hWnd, CHS_MSG_HUNT_MACHINE, 3, (LPARAM)(void*)newMach);
	}
	else Hunt(newMach);

	dataProcessed = true;		
}

//---------------------------------------------------------------------------

void ScanMethodHunt::Hunt(MMachineInfo *newMach)
{
	// now hunt it
	if(hWnd!=NULL)
		SendMessage(hWnd, CHS_MSG_HUNT_MACHINE, 1, (LPARAM)(void*)newMach);

	if(xHuntParams.xPerformSynScan){
		sspc.sspData = xHuntParams.xSynScanPorts;
		scanMethodSyn.Start(&sspc);
		scanMethodSyn.Stop();

		if(hWnd!=NULL)
			SendMessage(hWnd, CHS_MSG_UPDATE_DATA, 2, 0);
	}

	if(xHuntParams.xPerformDnsScan){
		sspc.sspData = _T("");
		scanMethodDns.Start(&sspc);
		scanMethodDns.Stop();

		// e ultima din lista nu dau refresh ca da la sfrasit automat !
		//if(hWnd!=NULL)
		//	SendMessage(hWnd, CHS_MSG_UPDATE_DATA, 2, 0);
	}

	newMach->hunted = true;

	if(hWnd!=NULL)
		SendMessage(hWnd, CHS_MSG_HUNT_MACHINE, 2, (LPARAM)(void*)newMach);
}

//---------------------------------------------------------------------------
